from datetime import datetime
from logging import Logger

from x_pc.db.kwms import Session
from x_pc.db.kwms.model import KwmsWorkQueue, KwmsQueuePoolRelation, KwmsWorkPool


class KwmsService(object):
    """
    排程输出服务
    """

    @staticmethod
    def queue_validate(_: KwmsWorkQueue, logger: Logger):
        """
        校验数据是否正常(符合数据保存策略)
        """
        if not _.wops_wavelet_orderid:
            warning_msg = f'[PC] 数据保存异常 ({_.command_group_code}){_.task_id} 无小波次数据'
            logger.warning(warning_msg)
            return False
        return True

    @staticmethod
    def save_data_v2(queue_list: list[KwmsWorkQueue], logger: Logger) -> list[tuple[str, str]]:
        # 数据校验
        queue_list = [_ for _ in queue_list if KwmsService.queue_validate(_, logger)]
        if not queue_list:
            logger.warning('[PC] 排程数据保存 无数据')
            return []

        with Session() as sess:
            try:
                info_msg = f'[PC] 排程数据保存 开始, 总数: {len(queue_list)}'
                logger.info(info_msg)
                sess.begin()
                # 批量删除 queue
                task_ids = [q.task_id for q in queue_list]
                sess.query(KwmsWorkQueue).filter(KwmsWorkQueue.task_id.in_(task_ids)).delete()
                # 批量删除
                sub_task_relation = sess.query(KwmsQueuePoolRelation).filter(
                    KwmsQueuePoolRelation.wops_kwms_work_queue_task_id.in_(task_ids)).all()
                sub_task_ids = [relation.wops_kwms_work_pool_sub_task_id for relation in sub_task_relation]
                # 批量删除 relation
                sess.query(KwmsQueuePoolRelation).filter(
                    KwmsQueuePoolRelation.wops_kwms_work_queue_task_id.in_(task_ids)).delete()
                # 批量删除 pool
                sess.query(KwmsWorkPool).filter(KwmsWorkPool.sub_task_id.in_(sub_task_ids)).delete()

                # 批量写入 queue pool
                sess.add_all(queue_list)
                ext_pool_list = [ext_pool for q in queue_list for ext_pool in q.ext_pool_list]
                sess.add_all(ext_pool_list)
                # 批量写入 relation
                relations = [KwmsQueuePoolRelation(q.task_id, ext_pool.sub_task_id) for q in queue_list for ext_pool in
                             q.ext_pool_list]
                sess.bulk_save_objects(relations)

                # 提交事务
                sess.commit()

                info_msg = f'[PC] 排程数据保存 完成, 总数: {len(queue_list)}'
                logger.info(info_msg)
                return [(_.command_group_code, _.task_id.replace(_.command_group_code + '_', '')) for _ in queue_list]
            except Exception as e:
                warning_msg = f'[PC] 排程数据保存 保存数据发生错误: {e}'
                logger.warning(warning_msg)
                sess.rollback()
            return []

    @staticmethod
    def get_pending_data(pending_ids) -> dict[str, list[tuple[float, float]]]:
        if not pending_ids:
            return {}
        pending_devices = {}
        with Session() as sess:
            # relation
            task_relation = sess.query(KwmsQueuePoolRelation).filter(
                KwmsQueuePoolRelation.wops_kwms_work_queue_task_id.in_(pending_ids)).all()
            task_ids = [relation.wops_kwms_work_pool_sub_task_id for relation in task_relation]
            # pool
            pool_list: list[KwmsWorkPool] = sess.query(KwmsWorkPool).filter(
                KwmsWorkPool.sub_task_id.in_(task_ids)).all()
        # 整理数据
        for pool in pool_list:
            key = f'{pool.equ_type}_{pool.equ_code}'  # TODO: LOC??
            if key not in pending_devices:
                pending_devices[key] = []
            pending_devices[key].append((datetime.timestamp(pool.plan_begin), datetime.timestamp(pool.plan_end)))
        # 对时间进行去重 # FIXME: 对于某些 如环穿? 会有多个设备情况
        # for k, v in pending_devices.items():
        #     pending_devices[k] = list(set(v))
        # 对时间进行排序
        for v in pending_devices.values():
            v.sort(key=lambda _: _[0])
        return pending_devices

    @staticmethod
    def fix_pending_task(pending_ids: list[str], base_time: float):
        """
        修正 锁定任务
        - 计划时间
        """
        if not pending_ids:
            return
        with Session() as sess:
            for pending_id in pending_ids:
                task_queue: KwmsWorkQueue = sess.query(KwmsWorkQueue).filter(
                    KwmsWorkQueue.task_id == pending_id).first()
                # relation
                task_relation = sess.query(KwmsQueuePoolRelation).filter(
                    KwmsQueuePoolRelation.wops_kwms_work_queue_task_id.in_(pending_ids)).all()
                task_ids = [relation.wops_kwms_work_pool_sub_task_id for relation in task_relation]
                # pool
                task_pool_list: list[KwmsWorkPool] = sess.query(KwmsWorkPool).filter(
                    KwmsWorkPool.sub_task_id.in_(task_ids)).all()

                # 过滤一下 sub_task_id 不是以 [下划线_ + 数字] 结尾的 (非本系统写入)
                task_pool_list = [_ for _ in task_pool_list if KwmsService.sub_task_id_match(_.sub_task_id)]

                # 处理数据
                task_pool_list.sort(key=lambda _: int(_.sub_task_id.split('_')[-1]))
                need_fix_pool_list = [_ for _ in task_pool_list if not _.real_end]
                # 无需要修改的子任务
                if not need_fix_pool_list:
                    continue
                # 修正下time_cost: decimal -> float
                for task_pool in need_fix_pool_list:
                    task_pool.time_cost = float(task_pool.time_cost)
                need_fix_first_pool = need_fix_pool_list[0]

                # 子任务 修改时间
                need_fix_first_pool.plan_end = datetime.fromtimestamp(base_time + need_fix_first_pool.time_cost)
                time_cost_sum = need_fix_first_pool.time_cost
                for need_fix_pool in need_fix_pool_list[1:]:
                    need_fix_pool.plan_begin = datetime.fromtimestamp(base_time + time_cost_sum)
                    time_cost_sum += need_fix_pool.time_cost
                    need_fix_pool.plan_end = datetime.fromtimestamp(base_time + time_cost_sum)
                sess.add_all(need_fix_pool_list)
                # 母任务 修改时间
                task_queue.plan_end = need_fix_pool_list[-1].plan_end
                sess.add(task_queue)
                #
                sess.commit()

    @staticmethod
    def sub_task_id_match(sub_task_id: str) -> bool:
        return sub_task_id.split('_') and sub_task_id.split('_')[-1] and sub_task_id.split('_')[-1].isdigit()
